# core/download_manager.py - 修复下载管理器
import json
import os
import threading
from pathlib import Path
from typing import List, Dict, Any, Optional, Callable
from cloudsync import CloudSync

from models.download_file import DownloadFile
from models.package_info import PackageInfo
from models.file_version import FileVersion
from utils.crypto_utils import calculate_hash
from utils.file_utils import generate_version_filename, ensure_directory_exists
from utils.validation_utils import validate_package_name, validate_version

class DownloadManager:
    """下载文件管理器"""
    
    def __init__(self, config, root):
        self.config = config
        self.root = root
        self.cloud_sync = None
        self.download_list: List[DownloadFile] = []
        self.dtemp_hash = ""
        self.download_queue = []
        self.is_initialized = False
        
        # 加载本地数据
        self.load_local_data()
    
    def initialize_cloud_sync(self):
        """初始化CloudSync实例"""
        if not self.cloud_sync:
            self.cloud_sync = CloudSync(self.config.webdav_url)
    
    def ensure_remote_structure(self) -> bool:
        """确保远程基础结构存在"""
        if not self.cloud_sync or not self.cloud_sync.is_authenticated:
            return False
        
        try:
            # 确保data目录存在
            self.cloud_sync.create_directory(self.config.data_dir)
            
            # 确保downloadfiles目录存在
            downloadfiles_path = f"{self.config.data_dir}/{self.config.downloadfiles_dir}"
            self.cloud_sync.create_directory(downloadfiles_path)
            
            # 确保有基础的downloadlist.json
            remote_downloadlist_path = f"{downloadfiles_path}/downloadlist.json"
            if not self.cloud_sync.file_exists(remote_downloadlist_path):
                # 创建空的downloadlist.json
                empty_list = {"files": []}
                temp_file = self.config.cache_dir / "temp_downloadlist.json"
                with open(temp_file, 'w', encoding='utf-8') as f:
                    json.dump(empty_list, f, ensure_ascii=False, indent=2)
                
                self.cloud_sync.upload_file(str(temp_file), remote_downloadlist_path)
                temp_file.unlink()
            
            # 确保有Dtemp.json
            remote_dtemp_path = f"{downloadfiles_path}/Dtemp.json"
            if not self.cloud_sync.file_exists(remote_dtemp_path):
                self.update_dtemp_file()
            
            return True
        
        except Exception as e:
            print(f"创建远程结构失败: {str(e)}")
            return False
    
    def load_local_data(self):
        """加载本地数据"""
        # 加载downloadlist.json
        local_downloadlist = self.config.cache_dir / "downloadlist.json"
        if local_downloadlist.exists():
            try:
                with open(local_downloadlist, 'r', encoding='utf-8') as f:
                    data = json.load(f)
                    self.download_list = [
                        DownloadFile.from_dict(item) for item in data.get("files", [])
                    ]
            except Exception:
                self.download_list = []
        
        # 加载Dtemp.json
        local_dtemp = self.config.cache_dir / "Dtemp.json"
        if local_dtemp.exists():
            try:
                with open(local_dtemp, 'r', encoding='utf-8') as f:
                    dtemp_data = json.load(f)
                    self.dtemp_hash = dtemp_data.get("hash", "")
            except Exception:
                self.dtemp_hash = ""
    
    def sync_with_remote(self, callback: Callable = None) -> bool:
        """与远程同步数据"""
        if not self.cloud_sync or not self.cloud_sync.is_authenticated:
            if callback:
                callback(False)
            return False
        
        def sync_task():
            try:
                # 确保远程结构存在
                if not self.ensure_remote_structure():
                    if callback:
                        self.root.after(0, lambda: callback(False))
                    return
                
                # 检查是否需要更新downloadlist
                remote_downloadlist_path = f"{self.config.data_dir}/{self.config.downloadfiles_dir}/downloadlist.json"
                local_downloadlist = self.config.cache_dir / "downloadlist.json"
                
                # 下载最新的downloadlist
                success = self.cloud_sync.download_file(
                    remote_downloadlist_path, str(local_downloadlist)
                )
                
                if success and local_downloadlist.exists():
                    with open(local_downloadlist, 'r', encoding='utf-8') as f:
                        data = json.load(f)
                    self.download_list = [
                        DownloadFile.from_dict(item) for item in data.get("files", [])
                    ]
                
                # 更新Dtemp.json
                self.update_dtemp_file()
                
                self.is_initialized = True
                
                if callback:
                    self.root.after(0, lambda: callback(True))
                
            except Exception as e:
                print(f"同步失败: {str(e)}")
                if callback:
                    self.root.after(0, lambda: callback(False))
        
        threading.Thread(target=sync_task, daemon=True).start()
        return True
    
    def update_dtemp_file(self):
        """更新Dtemp.json文件"""
        try:
            # 计算downloadlist的哈希
            downloadlist_content = json.dumps(
                {"files": [item.to_dict() for item in self.download_list]},
                ensure_ascii=False, indent=2
            )
            new_hash = calculate_hash(downloadlist_content)
            
            # 更新本地Dtemp.json
            local_dtemp = self.config.cache_dir / "Dtemp.json"
            dtemp_data = {"hash": new_hash}
            with open(local_dtemp, 'w', encoding='utf-8') as f:
                json.dump(dtemp_data, f, ensure_ascii=False, indent=2)
            
            # 上传到远程
            if self.cloud_sync and self.cloud_sync.is_authenticated:
                remote_dtemp_path = f"{self.config.data_dir}/{self.config.downloadfiles_dir}/Dtemp.json"
                self.cloud_sync.upload_file(str(local_dtemp), remote_dtemp_path)
            
            self.dtemp_hash = new_hash
            
        except Exception as e:
            print(f"更新Dtemp失败: {str(e)}")
    
    def check_dtemp_update(self) -> bool:
        """检查是否需要更新Dtemp"""
        if not self.cloud_sync or not self.cloud_sync.is_authenticated:
            return False
        
        try:
            remote_dtemp_path = f"{self.config.data_dir}/{self.config.downloadfiles_dir}/Dtemp.json"
            local_dtemp_temp = self.config.cache_dir / "Dtemp_temp.json"
            
            # 下载远程Dtemp.json
            success = self.cloud_sync.download_file(remote_dtemp_path, str(local_dtemp_temp))
            if success and local_dtemp_temp.exists():
                with open(local_dtemp_temp, 'r', encoding='utf-8') as f:
                    remote_dtemp = json.load(f)
                remote_hash = remote_dtemp.get("hash", "")
                
                local_dtemp_temp.unlink()
                
                if remote_hash != self.dtemp_hash:
                    # 需要更新
                    self.sync_with_remote()
                    return True
            
            return False
        
        except Exception:
            return False
    
    def get_file_list(self) -> List[DownloadFile]:
        """获取文件列表"""
        return self.download_list
    
    def search_files(self, keyword: str) -> List[DownloadFile]:
        """搜索文件（仅搜索名称）"""
        if not keyword:
            return self.download_list
        
        results = []
        keyword_lower = keyword.lower()
        
        for file_info in self.download_list:
            name = file_info.name.lower()
            if keyword_lower in name:
                results.append(file_info)
        
        return results
    
    def get_package_info(self, package_name: str) -> Optional[PackageInfo]:
        """获取包信息"""
        # 首先检查缓存
        from core.cache_manager import CacheManager
        cache_manager = CacheManager(self.config, self)
        cached_info = cache_manager.get_cached_info(package_name)
        
        if cached_info:
            return PackageInfo.from_dict(cached_info)
        
        # 如果缓存中没有，从远程下载
        try:
            remote_info_path = f"{self.config.data_dir}/{self.config.downloadfiles_dir}/{package_name}/info.json"
            local_info_temp = self.config.cache_dir / f"temp_{package_name}_info.json"
            
            success = self.cloud_sync.download_file(remote_info_path, str(local_info_temp))
            if success and local_info_temp.exists():
                with open(local_info_temp, 'r', encoding='utf-8') as f:
                    info_data = json.load(f)
                
                local_info_temp.unlink()
                return PackageInfo.from_dict(info_data)
        
        except Exception as e:
            print(f"获取包信息失败 {package_name}: {str(e)}")
        
        return None
    
    def create_new_package(self, package_info: PackageInfo, preview_image_path: str = None) -> bool:
        """创建新包"""
        if not validate_package_name(package_info.name):
            return False
        
        # 检查是否已存在
        for existing_file in self.download_list:
            if existing_file.name == package_info.name:
                return False
        
        try:
            # 创建远程目录
            package_remote_dir = f"{self.config.data_dir}/{self.config.downloadfiles_dir}/{package_info.name}"
            self.cloud_sync.create_directory(package_remote_dir)
            
            versions_remote_dir = f"{package_remote_dir}/versions"
            self.cloud_sync.create_directory(versions_remote_dir)
            
            # 上传info.json
            info_json = package_info.to_dict()
            temp_info = self.config.cache_dir / "temp_info.json"
            with open(temp_info, 'w', encoding='utf-8') as f:
                json.dump(info_json, f, ensure_ascii=False, indent=2)
            
            remote_info_path = f"{package_remote_dir}/info.json"
            self.cloud_sync.upload_file(str(temp_info), remote_info_path)
            temp_info.unlink()
            
            # 上传预览图
            if preview_image_path and os.path.exists(preview_image_path):
                remote_image_path = f"{package_remote_dir}/preview.png"
                self.cloud_sync.upload_file(preview_image_path, remote_image_path)
            
            # 更新downloadlist
            new_file = DownloadFile(
                name=package_info.name,
                brief=package_info.brief,
                description=package_info.description,
                preview_image="preview.png"
            )
            self.download_list.append(new_file)
            
            # 更新downloadlist.json和Dtemp.json
            self.update_downloadlist_file()
            self.update_dtemp_file()
            
            return True
        
        except Exception as e:
            print(f"创建新包失败: {str(e)}")
            return False
    
    def update_package_info(self, package_name: str, brief: str = None, 
                          description: str = None, preview_image_path: str = None) -> bool:
        """更新包信息"""
        package_info = self.get_package_info(package_name)
        if not package_info:
            return False
        
        try:
            # 更新信息
            if brief is not None:
                package_info.brief = brief
            if description is not None:
                package_info.description = description
            
            package_info.updated_time = PackageInfo().updated_time  # 使用新的时间
            
            # 上传更新后的info.json
            info_json = package_info.to_dict()
            temp_info = self.config.cache_dir / "temp_info.json"
            with open(temp_info, 'w', encoding='utf-8') as f:
                json.dump(info_json, f, ensure_ascii=False, indent=2)
            
            remote_info_path = f"{self.config.data_dir}/{self.config.downloadfiles_dir}/{package_name}/info.json"
            self.cloud_sync.upload_file(str(temp_info), remote_info_path)
            temp_info.unlink()
            
            # 上传新的预览图
            if preview_image_path and os.path.exists(preview_image_path):
                remote_image_path = f"{self.config.data_dir}/{self.config.downloadfiles_dir}/{package_name}/preview.png"
                self.cloud_sync.upload_file(preview_image_path, remote_image_path)
            
            # 更新downloadlist中的信息
            for file_info in self.download_list:
                if file_info.name == package_name:
                    if brief is not None:
                        file_info.brief = brief
                    if description is not None:
                        file_info.description = description
                    file_info.updated_time = package_info.updated_time
                    break
            
            # 更新downloadlist.json和Dtemp.json
            self.update_downloadlist_file()
            self.update_dtemp_file()
            
            return True
        
        except Exception as e:
            print(f"更新包信息失败: {str(e)}")
            return False
    
    def add_version_to_package(self, package_name: str, version: FileVersion, 
                             local_file_path: str) -> bool:
        """添加版本到包"""
        if not validate_version(version.version):
            return False
        
        package_info = self.get_package_info(package_name)
        if not package_info:
            return False
        
        # 检查版本是否已存在
        if version.version in package_info.versions:
            return False
        
        try:
            # 生成版本文件名
            version_file_name = generate_version_filename(
                package_name, version.version, local_file_path
            )
            version.file_name = version_file_name
            
            # 获取文件大小
            version.file_size = os.path.getsize(local_file_path)
            
            # 上传版本文件
            remote_version_path = f"{self.config.data_dir}/{self.config.downloadfiles_dir}/{package_name}/versions/{version_file_name}"
            self.cloud_sync.upload_file(local_file_path, remote_version_path)
            
            # 更新包信息
            package_info.add_version(version)
            
            # 上传更新后的info.json
            info_json = package_info.to_dict()
            temp_info = self.config.cache_dir / "temp_info.json"
            with open(temp_info, 'w', encoding='utf-8') as f:
                json.dump(info_json, f, ensure_ascii=False, indent=2)
            
            remote_info_path = f"{self.config.data_dir}/{self.config.downloadfiles_dir}/{package_name}/info.json"
            self.cloud_sync.upload_file(str(temp_info), remote_info_path)
            temp_info.unlink()
            
            # 更新downloadlist中的更新时间
            for file_info in self.download_list:
                if file_info.name == package_name:
                    file_info.updated_time = package_info.updated_time
                    break
            
            # 更新downloadlist.json和Dtemp.json
            self.update_downloadlist_file()
            self.update_dtemp_file()
            
            return True
        
        except Exception as e:
            print(f"添加版本失败: {str(e)}")
            return False
    
    def update_version_info(self, package_name: str, version: str, 
                          brief: str = None, description: str = None) -> bool:
        """更新版本信息"""
        package_info = self.get_package_info(package_name)
        if not package_info or version not in package_info.versions:
            return False
        
        try:
            # 更新版本信息
            package_info.update_version(version, brief, description)
            
            # 上传更新后的info.json
            info_json = package_info.to_dict()
            temp_info = self.config.cache_dir / "temp_info.json"
            with open(temp_info, 'w', encoding='utf-8') as f:
                json.dump(info_json, f, ensure_ascii=False, indent=2)
            
            remote_info_path = f"{self.config.data_dir}/{self.config.downloadfiles_dir}/{package_name}/info.json"
            self.cloud_sync.upload_file(str(temp_info), remote_info_path)
            temp_info.unlink()
            
            # 更新downloadlist中的更新时间
            for file_info in self.download_list:
                if file_info.name == package_name:
                    file_info.updated_time = package_info.updated_time
                    break
            
            # 更新downloadlist.json和Dtemp.json
            self.update_downloadlist_file()
            self.update_dtemp_file()
            
            return True
        
        except Exception as e:
            print(f"更新版本信息失败: {str(e)}")
            return False
    
    def download_version_file(self, package_name: str, version: str, 
                            save_path: str, callback: Callable = None) -> bool:
        """下载版本文件"""
        package_info = self.get_package_info(package_name)
        if not package_info or version not in package_info.versions:
            if callback:
                callback(False)
            return False
        
        version_info = package_info.versions[version]
        remote_file_path = f"{self.config.data_dir}/{self.config.downloadfiles_dir}/{package_name}/versions/{version_info.file_name}"
        
        def download_task():
            try:
                success = self.cloud_sync.download_file(remote_file_path, save_path)
                if callback:
                    self.root.after(0, lambda: callback(success))
                return success
            except Exception as e:
                print(f"下载文件失败: {str(e)}")
                if callback:
                    self.root.after(0, lambda: callback(False))
                return False
        
        threading.Thread(target=download_task, daemon=True).start()
        return True
    
    def update_downloadlist_file(self):
        """更新downloadlist.json文件"""
        try:
            # 更新本地downloadlist.json
            local_downloadlist = self.config.cache_dir / "downloadlist.json"
            downloadlist_data = {
                "files": [item.to_dict() for item in self.download_list]
            }
            with open(local_downloadlist, 'w', encoding='utf-8') as f:
                json.dump(downloadlist_data, f, ensure_ascii=False, indent=2)
            
            # 上传到远程
            if self.cloud_sync and self.cloud_sync.is_authenticated:
                remote_downloadlist_path = f"{self.config.data_dir}/{self.config.downloadfiles_dir}/downloadlist.json"
                self.cloud_sync.upload_file(str(local_downloadlist), remote_downloadlist_path)
        
        except Exception as e:
            print(f"更新downloadlist失败: {str(e)}")
    def update_version_info(self, package_name: str, version: str, brief: str, description: str) -> bool:
        """更新版本信息"""
        package_info = self.get_package_info(package_name)
        if not package_info or version not in package_info.versions:
            return False
        
        try:
            # 更新版本信息
            package_info.versions[version].brief = brief
            package_info.versions[version].description = description
            
            # 上传更新后的info.json
            info_json = package_info.to_dict()
            temp_info = self.config.cache_dir / "temp_info.json"
            with open(temp_info, 'w', encoding='utf-8') as f:
                json.dump(info_json, f, ensure_ascii=False, indent=2)
            
            remote_info_path = f"{self.config.data_dir}/{self.config.downloadfiles_dir}/{package_name}/info.json"
            self.cloud_sync.upload_file(str(temp_info), remote_info_path)
            temp_info.unlink()
            
            # 更新downloadlist中的更新时间
            for file_info in self.download_list:
                if file_info.name == package_name:
                    file_info.updated_time = package_info.updated_time
                    break
            
            # 更新downloadlist.json和Dtemp.json
            self.update_downloadlist_file()
            self.update_dtemp_file()
            
            return True
        
        except Exception as e:
            self.logger.error(f"更新版本信息失败: {str(e)}")
            return False        